package com.kakarote.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.TypeUtils;
import com.kakarote.common.field.entity.ModelFieldExtend;
import com.kakarote.common.field.utils.FieldUtil;
import com.kakarote.common.log.entity.OperationLog;
import com.kakarote.core.common.Const;
import com.kakarote.core.common.cache.CrmCacheKey;
import com.kakarote.core.common.enums.FieldEnum;
import com.kakarote.core.common.enums.SystemCodeEnum;
import com.kakarote.core.entity.BasePage;
import com.kakarote.core.exception.CrmException;
import com.kakarote.core.feign.admin.entity.FileEntity;
import com.kakarote.core.feign.admin.entity.SimpleDept;
import com.kakarote.core.feign.admin.entity.SimpleUser;
import com.kakarote.core.feign.examine.entity.ExamineInfoVo;
import com.kakarote.core.feign.examine.service.ExamineService;
import com.kakarote.core.servlet.BaseServiceImpl;
import com.kakarote.core.utils.BaseUtil;
import com.kakarote.core.utils.UserCacheUtil;
import com.kakarote.oa.common.LanguageFieldUtil;
import com.kakarote.oa.common.OaCodeEnum;
import com.kakarote.oa.common.OaExamineStatusEnum;
import com.kakarote.oa.common.OaExamineTypeEnum;
import com.kakarote.oa.constart.OaConst;
import com.kakarote.oa.entity.BO.GetExamineFieldBO;
import com.kakarote.oa.entity.BO.OaExamineRecordLogDetailBO;
import com.kakarote.oa.entity.BO.OaPrintTemplateQueryBO;
import com.kakarote.oa.entity.PO.OaExamineField;
import com.kakarote.oa.entity.PO.OaExaminePrintTemplate;
import com.kakarote.oa.entity.PO.OaExamineTravel;
import com.kakarote.oa.entity.VO.ExamineFlowDataVO;
import com.kakarote.oa.entity.VO.ExamineVO;
import com.kakarote.oa.entity.VO.OaExaminePrintFieldVO;
import com.kakarote.oa.entity.VO.OaLogTemplateFieldVO;
import com.kakarote.oa.mapper.OaExaminePrintTemplateMapper;
import com.kakarote.oa.service.IOaExamineFieldService;
import com.kakarote.oa.service.IOaExaminePrintTemplateService;
import com.kakarote.oa.service.IOaExamineService;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author wwl
 * @date 2022/7/15 10:40
 */
@Service
public class OaExaminePrintTemplateServiceImpl extends BaseServiceImpl<OaExaminePrintTemplateMapper, OaExaminePrintTemplate> implements IOaExaminePrintTemplateService {

    @Autowired
    private IOaExamineFieldService oaExamineFieldService;
    @Autowired
    private IOaExamineService oaExamineService;
    @Autowired
    private ExamineService examineService;


    /**
     * 分页查询打印模板列表
     *
     * @param templateBO search
     * @return data
     */
    @Override
    public BasePage<OaExaminePrintTemplate> queryList(OaPrintTemplateQueryBO templateBO) {
        BasePage<OaExaminePrintTemplate> page = this.getBaseMapper().queryList(templateBO.parse(), templateBO.getConfigId());
        page.getList().forEach(f -> {
            f.setCreateUserName(ObjectUtil.equal(0L, f.getCreateUserId()) ? "系统默认" : UserCacheUtil.getUserName(f.getCreateUserId()));
            Map<String, String> keyMap = new HashMap<>();
            keyMap.put("examineName_resourceKey", "oa.examine.print.template.name." + LanguageFieldUtil.getFieldName(f.getExamineName()));
            f.setLanguageKeyMap(keyMap);
        });
        return page;
    }

    /**
     * 删除打印模板
     *
     * @param templateId templateId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deletePrintTemplate(Long templateId) {
        removeById(templateId);
    }

    /**
     * 查询字段列表
     *
     * @param templateId type
     * @return data
     */
    @Override
    public OaExaminePrintFieldVO queryFields(Long templateId) {
        ExamineInfoVo examineInfoVo = examineService.queryExamineById(templateId).getData();
        // 只有label==0 即oa的审批需要打印
        if (ObjectUtil.notEqual(0, examineInfoVo.getLabel())) {
            return null;
        }
        // 返回对象
        OaExaminePrintFieldVO vo = new OaExaminePrintFieldVO();
        Integer oaType = examineInfoVo.getOaType();
        // 审批类型 前端用来区分哪些审批需要特殊的表格字段
        vo.setOaType(oaType);
        // 不打印的字段
        List<Integer> notPrintFields = Arrays.asList(
                FieldEnum.FIELD_GROUP.getType()
                , FieldEnum.BOOLEAN_VALUE.getType()
                , FieldEnum.HANDWRITING_SIGN.getType()
                , FieldEnum.DESC_TEXT.getType()
                // , FieldEnum.DETAIL_TABLE.getType()
                , FieldEnum.ATTENTION.getType()
                , FieldEnum.TAG.getType()
                // 去掉行程明细、费用明细字段
                , FieldEnum.EXAMINE_CAUSE.getType()
                , FieldEnum.BUSINESS_CAUSE.getType()
        );
        List<OaExamineField> oaExamineFields = oaExamineFieldService.queryField(templateId, Boolean.FALSE);
        oaExamineFields.removeIf(field -> notPrintFields.contains(field.getType()));
        List<OaLogTemplateFieldVO> fields = new ArrayList<>();
        oaExamineFields.forEach(f -> {
            OaLogTemplateFieldVO fieldVO = BeanUtil.copyProperties(f, OaLogTemplateFieldVO.class);
            fields.add(fieldVO);
            if (ObjectUtil.equal(f.getType(), FieldEnum.DETAIL_TABLE.getType())) {
                List<? extends ModelFieldExtend> sonFields = f.getFieldExtendList();
                sonFields.forEach(son -> {
                    OaLogTemplateFieldVO sonVO = BeanUtil.copyProperties(son, OaLogTemplateFieldVO.class);
                    sonVO.setFieldId(son.getId());
                    fields.add(sonVO);
                });
            }
        });
        HashMap<String, String> createUserMap = new HashMap<>();
        createUserMap.put("name_resourceKey", "oa.log.template.field.createUserName");
        HashMap<String, String> createTimeMap = new HashMap<>();
        createTimeMap.put("name_resourceKey", "oa.log.template.field.createTime");
        HashMap<String, String> updateTimeMap = new HashMap<>();
        updateTimeMap.put("name_resourceKey", "oa.log.template.field.updateTime");
        fields.add(new OaLogTemplateFieldVO("createUserName", "创建人").setLanguageKeyMap(createUserMap));
        fields.add(new OaLogTemplateFieldVO("createTime", "创建时间").setLanguageKeyMap(createTimeMap));
        fields.add(new OaLogTemplateFieldVO("updateTime", "更新时间").setLanguageKeyMap(updateTimeMap));
        vo.setExamineFields(fields);
        OaExamineTypeEnum parse = OaExamineTypeEnum.parse(oaType);
        List<OaLogTemplateFieldVO> travelFields = new ArrayList<>();
        switch (parse) {
            // 出差
            case BUSINESS_TRIP: {
                travelFields.add(new OaLogTemplateFieldVO("startAddress", "出发城市"));
                travelFields.add(new OaLogTemplateFieldVO("endAddress", "目的城市"));
                travelFields.add(new OaLogTemplateFieldVO("trip", "单程往返"));
                travelFields.add(new OaLogTemplateFieldVO("vehicle", "交通工具"));
                travelFields.add(new OaLogTemplateFieldVO("endTime", "结束时间"));
                travelFields.add(new OaLogTemplateFieldVO("startTime", "开始时间"));
                travelFields.add(new OaLogTemplateFieldVO("duration", "时长（天）"));
                travelFields.add(new OaLogTemplateFieldVO("description", "备注"));
                break;
            }
            // 差旅报销
            case TRAVEL_REIMBURSEMENT: {
                travelFields.add(new OaLogTemplateFieldVO("startAddress", "出发城市"));
                travelFields.add(new OaLogTemplateFieldVO("endAddress", "目的城市"));
                travelFields.add(new OaLogTemplateFieldVO("startTime", "开始时间"));
                travelFields.add(new OaLogTemplateFieldVO("endTime", "结束时间"));
                travelFields.add(new OaLogTemplateFieldVO("traffic", "交通费"));
                travelFields.add(new OaLogTemplateFieldVO("diet", "餐饮费"));
                travelFields.add(new OaLogTemplateFieldVO("stay", "住宿费"));
                travelFields.add(new OaLogTemplateFieldVO("other", "其他费用"));
                travelFields.add(new OaLogTemplateFieldVO("money", "合计（元）"));
                travelFields.add(new OaLogTemplateFieldVO("description", "费用明细描述"));
                break;
            }
            default:
                break;
        }
        for (OaLogTemplateFieldVO travelField : travelFields) {
            HashMap<String, String> keyMap = new HashMap<>();
            keyMap.put("name_resourceKey", "oa.examine.record.field." + travelField.getFieldName());
            travelField.setLanguageKeyMap(keyMap);
        }
        vo.setTravelFields(travelFields);
        // 审批信息字段
        List<OaLogTemplateFieldVO> recordFields = new ArrayList<>();
        recordFields.add(new OaLogTemplateFieldVO("flowName", "节点名称"));
        recordFields.add(new OaLogTemplateFieldVO("realname", "审批人"));
        recordFields.add(new OaLogTemplateFieldVO("examineStatus", "审批结果"));
        recordFields.add(new OaLogTemplateFieldVO("remark", "审批意见"));
        recordFields.add(new OaLogTemplateFieldVO("examineTime", "审批时间"));
        for (OaLogTemplateFieldVO recordField : recordFields) {
            HashMap<String, String> keyMap = new HashMap<>();
            keyMap.put("name_resourceKey", "oa.examine.record.field." + recordField.getFieldName());
            recordField.setLanguageKeyMap(keyMap);
        }
        vo.setRecordFields(recordFields);
        return vo;
    }

    /**
     * 打印
     *
     * @param templateId templateId
     * @param id         id
     * @return data
     */
    @Override
    public String print(Long templateId, Long id) {
        OaExaminePrintTemplate template = getById(templateId);
        if (StrUtil.isEmpty(template.getContent())) {
            throw new CrmException(OaCodeEnum.CRM_PRINT_TEMPLATE_NOT_EXIST_ERROR);
        }
        Long type = template.getConfigId();
        return replaceContent(id, template.getContent(), type);
    }

    /**
     * 预览
     *
     * @param content content
     * @param type    type
     * @return path
     */
    @Override
    public String preview(String content, String type) {
        String fileTypeP = "pdf";
        String fileTypeW = "word";
        if (!Arrays.asList(fileTypeP, fileTypeW).contains(type)) {
            throw new CrmException(OaCodeEnum.CRM_PRINT_PRE_VIEW_ERROR);
        }
        String slash = BaseUtil.isWindows() ? "\\" : "/";
        String date = DateUtil.format(new Date(), "yyyyMMdd");
        String folderPath = FileUtil.getTmpDirPath() + "print" + slash + date;
        String UUID = IdUtil.simpleUUID();
        String fileName = UUID + ".pdf";
        FileUtil.mkdir(folderPath + slash);
        String path = folderPath + slash + fileName;
        String htmlName = UUID + ".html";
        String htmlPath = folderPath + slash + htmlName;
        JSONObject object = new JSONObject();
        String html = "<html>\n" +
                "<head>\n" +
                "<style>\n" +
                ".wk-watermark-dom {\n" +
                "-webkit-transform: rotate(-15deg) !important;\n" +
                (fileTypeW.equals(type) ? "display: none !important;\n" : "") +
                "}\n" +
                "body {\n" +
                "  line-height: 1.4;\n" +
                "  padding: 60px;\n" +
                "  width: 595px;\n" +
                "  margin: 0 auto;\n" +
                "  border-radius: 4px;\n" +
                "  background: white;\n" +
                "  min-height: 100%;\n" +
                "}\n" +
                "p { margin: 5px 0;\n" +
                "  line-height: 1.5;\n" +
                "}\n" +
                "table {\n" +
                "  border-collapse: collapse;\n" +
                "}\n" +
                "table th,\n" +
                "table td {\n" +
                "  border: 1px solid #ccc;\n" +
                "  padding: 0.4rem;\n" +
                "}\n" +
                "figure {\n" +
                "  display: table;\n" +
                "  margin: 1rem auto;\n" +
                "}\n" +
                "figure figcaption {\n" +
                "  color: #999;\n" +
                "  display: block;\n" +
                "  margin-top: 0.25rem;\n" +
                "  text-align: center;\n" +
                "}\n" +
                "hr {\n" +
                "  border-color: #ccc;\n" +
                "  border-style: solid;\n" +
                "  border-width: 1px 0 0 0;\n" +
                "}\n" +
                "code {\n" +
                "  background-color: #e8e8e8;\n" +
                "  border-radius: 3px;\n" +
                "  padding: 0.1rem 0.2rem;\n" +
                "}\n" +
                ".mce-content-body:not([dir=rtl]) blockquote {\n" +
                "  border-left: 2px solid #ccc;\n" +
                "  margin-left: 1.5rem;\n" +
                "  padding-left: 1rem;\n" +
                "}\n" +
                ".mce-content-body[dir=rtl] blockquote {\n" +
                "  border-right: 2px solid #ccc;\n" +
                "  margin-right: 1.5rem;\n" +
                "  padding-right: 1rem;\n" +
                "}\n" +
                "</style>\n" +
                "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">" +
                "</head>\n" +
                "<body>\n" +
                content +
                "</body>\n" +
                "</html>";
        FileUtil.writeUtf8String(html, htmlPath);
        if (fileTypeP.equals(type)) {
            writePDFContent(htmlPath, path);
        } else if (fileTypeW.equals(type)) {
            String filePath = folderPath + slash + UUID + ".doc";
            File file = FileUtil.file(filePath);
            try (
                    ByteArrayInputStream byteArrayInputStream = IoUtil.toUtf8Stream(html);
                    FileOutputStream outputStream = new FileOutputStream(file);
                    POIFSFileSystem poifsFileSystem = new POIFSFileSystem();
            ) {
                DirectoryEntry directoryEntry = poifsFileSystem.getRoot();
                directoryEntry.createDocument("WordDocument", byteArrayInputStream);
                object.put("word", file.getAbsolutePath());
                poifsFileSystem.writeFilesystem(outputStream);
            } catch (Exception e) {
                log.error("打印预览转换word失败", e);
                throw new CrmException(SystemCodeEnum.SYSTEM_ERROR);
            }
            writePDFContent(htmlPath, path);
        }
        String uuid = IdUtil.simpleUUID();
        object.put("pdf", path);
        BaseUtil.getRedis().setex(CrmCacheKey.CRM_PRINT_TEMPLATE_CACHE_KEY + uuid, 3600 * 24, object.toJSONString());
        return uuid;
    }

    private void writePDFContent(String content, String distPath) {
        log.info("准备转换pdf");
        String toPdfTool;
        if (BaseUtil.isWindows()) {
            toPdfTool = OaConst.WIN_TO_PDF_TOOL;
        } else {
            toPdfTool = OaConst.LINUX_TO_PDF_TOOL;
        }
        StringBuilder cmd = new StringBuilder();
        log.info("设置插件地址 ： " + toPdfTool);
        cmd.append(toPdfTool);
        cmd.append(" ");
        cmd.append(" --log-level none ");
        cmd.append(content);
        cmd.append(" ");
        cmd.append(distPath);
        try {
            log.info("进行转换,html地址 ： " + content);
            log.info("进行转换,转换后pdf地址 ： " + distPath);
            Process process = Runtime.getRuntime().exec(cmd.toString());
            process.waitFor();
            log.info("转换成功 ");
            process.destroy();
        } catch (Exception e) {
            log.info("转换失败，失败原因" + e);
        }
    }

    private String replaceContent(Long examineId, String content, Long templateId) {
        List<String> spanList = ReUtil.findAllGroup0("<span((?!/span>).)*data-wk-tag=\".*?\".*?</span>", content);
        List<String> tableStyleList = ReUtil.findAllGroup0("<table[^(<|>)]*>", content);
        for (String tableStyle : tableStyleList) {
            String newStyle;
            if (tableStyle.contains("float: right") || tableStyle.contains("margin-left: auto; margin-right: auto;")) {
                newStyle = tableStyle.replace(">", " align=\"right\">");
            } else {
                newStyle = tableStyle.replace(">", " align=\"left\">");
            }
            content = content.replaceAll(tableStyle, newStyle);
        }
        List<String> tableList = ReUtil.findAllGroup0("<table.*?data-wk-table-tag=\"table\".*?table>", content);
        Map<String, String> map = new HashMap<>();
        // 审批的字段
        List<OaExamineField> logTemplateFieldVOList = oaExamineFieldService.queryField(templateId, Boolean.FALSE);
        Map<String, Integer> fieldName2TypeMap = logTemplateFieldVOList
                .stream()
                .collect(Collectors.toMap(OaExamineField::getFieldName, OaExamineField::getType));
        Map<String, Integer> newMap = new HashMap<>();
        for (Map.Entry<String, Integer> entry : fieldName2TypeMap.entrySet()) {
            newMap.put(StrUtil.toCamelCase(entry.getKey()), entry.getValue());
        }
        // 审批详情
        ExamineVO examineVO = oaExamineService.queryOaExamineInfo(examineId.toString());
        Map<String, Object> record = BeanUtil.copyProperties(examineVO, Map.class);
        record.put("createUserName", examineVO.getCreateUser().getRealname());
        GetExamineFieldBO queryBO = new GetExamineFieldBO();
        queryBO.setExamineId(examineId);
        queryBO.setType("examineId");
        queryBO.setIsDetail(1);
        // 自定义字段值list
        List<OaExamineField> fields = oaExamineService.getField(queryBO);
        fields.forEach(f -> record.put(StrUtil.toCamelCase(f.getFieldName()), f.getValue()));
        spanList.forEach(span -> {
            String key = ReUtil.getGroup1("<span.*?data-wk-tag=\"(.*?)\".*?</span>", span);
            String configId = ReUtil.getGroup1("^([A-Za-z]*)\\.", key);
            String fieldName = ReUtil.delFirst("^[A-Za-z]*\\.", key);
            fieldName = StrUtil.toCamelCase(fieldName);
            String value;
            if (newMap.containsKey(fieldName)) {
                Integer formType = newMap.get(fieldName);
                getName(record, fieldName, formType);
            }
            if (ObjectUtil.isEmpty(record.get(fieldName))) {
                value = "";
            } else {
                if (record.get(fieldName) instanceof Date) {
                    value = DateUtil.formatDateTime((Date) record.get(fieldName));
                } else if (record.get(fieldName) instanceof LocalDateTime) {
                    value = LocalDateTimeUtil.formatNormal((LocalDateTime) record.get(fieldName));
                } else {
                    value = TypeUtils.castToString(record.get(fieldName));
                }
            }
            map.put(key, value);
        });
        // 差旅
        List<OaExamineTravel> travelList = examineVO.getExamineTravelList();
        content = appendTableContent(content, tableList, travelList, examineVO.getExamineRecordId(), fields);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String k = entry.getKey();
            String v = StrUtil.isNotEmpty(entry.getValue()) ? entry.getValue() : "";
            content = content.replaceAll("(<span((?!/span>).)*data-wk-tag=\"" + k + "\".*?)(\\{.*?\\})(</span>)", "$1" + v + "$4");
        }
        return content;

    }

    private String appendTableContent(String content, List<String> tableList, List<OaExamineTravel> travelList, Long examineRecordId, List<OaExamineField> fields) {
        if (CollUtil.isEmpty(tableList)) {
            return content;
        }
        Map<Long, OaExamineField> fieldId2FieldMap = fields.stream().filter(f -> CollUtil.isNotEmpty(f.getFieldExtendList())).collect(Collectors.toMap(OaExamineField::getFieldId, Function.identity()));
        Class<? extends OaExamineTravel> travelClass = OaExamineTravel.class;
        Field[] travelClassDeclaredFields = travelClass.getDeclaredFields();
        Class<? extends OaExamineRecordLogDetailBO> detailBOClass = OaExamineRecordLogDetailBO.class;
        Field[] detailBOClassDeclaredFields = detailBOClass.getDeclaredFields();
        for (String table : tableList) {
            String tableMid = ReUtil.getGroup0("<tr((?!header).)*data-wk-table-tr-tag=\"value\">.*?</tr>?", table);
            List<String> spanList = ReUtil.findAllGroup0("<span.*?data-wk-table-value-tag=\".*?\".*?</span>", tableMid);
            StringBuilder tableContent = new StringBuilder();
            if (CollUtil.isNotEmpty(spanList)) {
                String spanStr = spanList.get(0);
                String tableKeyStr = ReUtil.getGroup1("<span.*?data-wk-table-value-tag=\"(.*?)\".*?</span>", spanStr);
                // 费用报销表格
                if (StrUtil.startWith(tableKeyStr, "travelFields")) {
                    for (OaExamineTravel travel : travelList) {
                        Map<String, String> travelMap = new HashMap<>();
                        spanList.forEach(span -> {
                            String tableKey = ReUtil.getGroup1("<span.*?data-wk-table-value-tag=\"(.*?)\".*?</span>", span);
                            String fieldName = ReUtil.delFirst("^[A-Za-z]*\\.", tableKey);
                            StringBuilder value = new StringBuilder("");
                            fieldName = StrUtil.toCamelCase(fieldName);
                            for (Field declareField : travelClassDeclaredFields) {
                                declareField.setAccessible(true);
                                if (ObjectUtil.equal(declareField.getName(), fieldName)) {
                                    Object declareFieldValue = null;
                                    try {
                                        declareFieldValue = declareField.get(travel);
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    }
                                    if (ObjectUtil.isNotNull(declareFieldValue)) {
                                        if (declareFieldValue instanceof Date) {
                                            value = new StringBuilder(DateUtil.formatDateTime((Date) declareFieldValue));
                                        } else if (declareFieldValue instanceof LocalDateTime) {
                                            value = new StringBuilder(LocalDateTimeUtil.formatNormal((LocalDateTime) declareFieldValue));
                                        } else {
                                            value = new StringBuilder(declareFieldValue.toString());
                                        }
                                        break;
                                    }
                                }
                            }
                            travelMap.put(tableKey, value.toString());
                        });
                        String newTableMid = tableMid;
                        for (Map.Entry<String, String> entry : travelMap.entrySet()) {
                            String k = entry.getKey();
                            String v = StrUtil.isNotEmpty(entry.getValue()) ? entry.getValue() : "";
                            newTableMid = newTableMid.replaceAll("(<span((?!/span>).)*data-wk-table-value-tag=\"" + k + "\".*?)(\\{.*?\\})(</span>)", "$1" + v + "$4");
                        }
                        tableContent.append(newTableMid);
                    }
                }
                // 审批记录表格
                else if (StrUtil.startWith(tableKeyStr, "recordFields")) {
                    if (ObjectUtil.isNotNull(examineRecordId)) {
                        JSONObject data = examineService.queryExamineRecordList(examineRecordId, null).getData();
                        List<Map<String, Object>> examineFlowList = (List<Map<String, Object>>) data.get("examineFlowList");
                        List<OaExamineRecordLogDetailBO> detailList = new ArrayList<>();
                        examineFlowList.forEach(f -> {
                            ExamineFlowDataVO flowDataVO = BeanUtil.copyProperties(f, ExamineFlowDataVO.class);
                            if (ObjectUtil.notEqual(0L, flowDataVO.getFlowId())) {
                                List<Map<String, Object>> userList = flowDataVO.getUserList();
                                userList.forEach(u -> {
                                    OaExamineRecordLogDetailBO detailBO = new OaExamineRecordLogDetailBO();
                                    detailBO.setFlowName(flowDataVO.getName());
                                    Object examineStatus = u.get("examineStatus");
                                    if (ObjectUtil.isNotNull(examineStatus)) {
                                        String statusStr = OaExamineStatusEnum.parse((Integer) examineStatus).getMsg();
                                        detailBO.setExamineStatus(statusStr);
                                    }
                                    detailBO.setExamineTime(ObjectUtil.isNotNull(u.get("examineTime")) ? u.get("examineTime").toString() : "");
                                    detailBO.setRealname(ObjectUtil.isNotNull(u.get("realname")) ? u.get("realname").toString() : "");
                                    detailBO.setRemark(ObjectUtil.isNotNull(u.get("remarks")) ? u.get("remarks").toString() : "");
                                    detailList.add(detailBO);
                                });
                            }
                        });
                        if (CollUtil.isNotEmpty(detailList)) {
                            for (OaExamineRecordLogDetailBO detailBO : detailList) {
                                Map<String, String> travelMap = new HashMap<>();
                                spanList.forEach(span -> {
                                    String tableKey = ReUtil.getGroup1("<span.*?data-wk-table-value-tag=\"(.*?)\".*?</span>", span);
                                    String fieldName = ReUtil.delFirst("^[A-Za-z]*\\.", tableKey);
                                    StringBuilder value = new StringBuilder("");
                                    fieldName = StrUtil.toCamelCase(fieldName);

                                    for (Field declareField : detailBOClassDeclaredFields) {
                                        declareField.setAccessible(true);
                                        if (ObjectUtil.equal(declareField.getName(), fieldName)) {
                                            Object declareFieldValue = null;
                                            try {
                                                declareFieldValue = declareField.get(detailBO);
                                            } catch (IllegalAccessException e) {
                                                e.printStackTrace();
                                            }
                                            if (ObjectUtil.isNotNull(declareFieldValue)) {
                                                if (declareFieldValue instanceof Date) {
                                                    value = new StringBuilder(DateUtil.formatDateTime((Date) declareFieldValue));
                                                } else if (declareFieldValue instanceof LocalDateTime) {
                                                    value = new StringBuilder(LocalDateTimeUtil.formatNormal((LocalDateTime) declareFieldValue));
                                                } else {
                                                    value = new StringBuilder(declareFieldValue.toString());
                                                }
                                                break;
                                            }
                                        }
                                    }
                                    travelMap.put(tableKey, value.toString());
                                });
                                String newTableMid = tableMid;
                                for (Map.Entry<String, String> entry : travelMap.entrySet()) {
                                    String k = entry.getKey();
                                    String v = StrUtil.isNotEmpty(entry.getValue()) ? entry.getValue() : "";
                                    newTableMid = newTableMid.replaceAll("(<span((?!/span>).)*data-wk-table-value-tag=\"" + k + "\".*?)(\\{.*?\\})(</span>)", "$1" + v + "$4");
                                }
                                tableContent.append(newTableMid);
                            }
                        }
                    }
                }
                // 自定义明细表格
                else {
                    String[] fieldIds = tableKeyStr.split("\\.");
                    OaExamineField tableField = fieldId2FieldMap.get(TypeUtils.castToLong(fieldIds[0]));
                    if (ObjectUtil.isNull(tableField)) {
                        throw new CrmException(OaCodeEnum.PRINT_TEMPLATE_NOT_LATEST);
                    }
                    // 明细表格值
                    List<List<ModelFieldExtend>> tableValues = (List<List<ModelFieldExtend>>) tableField.getValue();
                    StringBuilder finalTableContent = tableContent;
                    tableValues.forEach(fieldList -> {
                        Map<String, String> key2ValueMap = new HashMap<>();
                        Map<Long, ModelFieldExtend> id2PatchMap = fieldList.stream().collect(Collectors.toMap(ModelFieldExtend::getId, Function.identity()));
                        spanList.forEach(span -> {
                            String tableKey = ReUtil.getGroup1("<span.*?data-wk-table-value-tag=\"(.*?)\".*?</span>", span);
                            String tableIdFieldIdStr = ReUtil.delFirst("^[A-Za-z]*\\.", tableKey);
                            String[] sonTableIdFieldId = tableIdFieldIdStr.split("\\.");
                            ModelFieldExtend fieldPatch = id2PatchMap.get(TypeUtils.castToLong(sonTableIdFieldId[1]));
                            if (ObjectUtil.isNotNull(fieldPatch)) {
                                String replaceValue = FieldUtil.parseDetailTableField(fieldPatch);
                                key2ValueMap.put(tableKey, replaceValue);
                            }
                        });
                        String newTableMid = tableMid;
                        for (Map.Entry<String, String> entry : key2ValueMap.entrySet()) {
                            String k = entry.getKey();
                            String v = StrUtil.isNotEmpty(entry.getValue()) ? entry.getValue() : "";
                            newTableMid = newTableMid.replaceAll("(<span((?!/span>).)*data-wk-table-value-tag=\"" + k + "\".*?)(\\{.*?\\})(</span>)", "$1" + v + "$4");
                        }
                        finalTableContent.append(newTableMid);
                    });
                }
            }
            tableContent = new StringBuilder(table.replaceAll("<tr.*?data-wk-table-tr-tag=\"value\">[\\s\\S]*</tr>?", tableContent.toString()));
            content = content.replace(table, tableContent.toString());
        }
        return content;
    }


    private void getName(Map<String, Object> record, String fieldName, Integer formType) {
        Object value = record.get(fieldName);
        if (ObjectUtil.isNotEmpty(value)) {
            if (formType.equals(FieldEnum.USER.getType())) {
                if (value instanceof List<?>) {
                    if (((List<?>) value).get(0) instanceof SimpleUser) {
                        record.put(fieldName, ((List<?>) value).stream().map(data -> ((SimpleUser) data).getRealname()).collect(Collectors.joining(Const.SEPARATOR)));
                    }

                } else if (value instanceof SimpleUser) {
                    record.put(fieldName, ((SimpleUser) value).getRealname());
                }

            } else if (formType.equals(FieldEnum.STRUCTURE.getType())) {
                List<SimpleDept> data = (List<SimpleDept>) value;
                record.put(fieldName, data.stream().map(SimpleDept::getName).collect(Collectors.joining(Const.SEPARATOR)));
            } else if (formType.equals(FieldEnum.FILE.getType())) {
                List<FileEntity> data = (List<FileEntity>) value;
                record.put(fieldName, data.stream().map(FileEntity::getName).collect(Collectors.joining(Const.SEPARATOR)));
            } else if (formType.equals(FieldEnum.AREA_POSITION.getType())) {
                JSONArray array = (JSONArray) value;
                StringBuilder str = new StringBuilder();
                for (int i = 0; i < array.size(); i++) {
                    JSONObject json = array.getJSONObject(i);
                    if (str.length() != 0) {
                        str.append(",");
                    }
                    str.append(json.getString("name"));
                }
                record.put(fieldName, str.toString());
            } else if (formType.equals(FieldEnum.CURRENT_POSITION.getType())) {
                JSONObject json = (JSONObject) value;
                record.put(fieldName, json.get("address"));
            }
        } else {
            record.put(fieldName, "");
        }
    }


    /**
     * 复制模板
     *
     * @param templateId templateId
     */
    @Override
    public OperationLog copyTemplate(Long templateId) {

        OaExaminePrintTemplate crmPrintTemplate = getById(templateId);
        String name = crmPrintTemplate.getTemplateName();
        String pre = ReUtil.delFirst("[(]\\d+[)]$", name);
        List<String> nameList;
        String regex = "^[(]\\d+[)]$";
        if (!ReUtil.contains(regex, name)) {
            nameList = query().like("template_name", pre).list().stream().map(OaExaminePrintTemplate::getTemplateName).collect(Collectors.toList());
        } else {
            nameList = query().last("  where template_name regexp '^[(]\\d+[)]$'").list().stream().map(OaExaminePrintTemplate::getTemplateName).collect(Collectors.toList());
        }
        StringBuilder numberSb = new StringBuilder();
        for (String dbName : nameList) {
            String endCode = ReUtil.get("[(]\\d+[)]$", dbName, 0);
            if (endCode != null) {
                numberSb.append(endCode);
            }
        }
        int i = 1;
        if (numberSb.length() == 0) {
            String leftIndex = "(";
            String rightIndex = ")";
            while (numberSb.toString().contains(leftIndex + i + rightIndex)) {
                i++;
            }
        }
        crmPrintTemplate.setTemplateName(pre + numberSb + "(1)");
        crmPrintTemplate.setTemplateId(null);
        save(crmPrintTemplate);

        OperationLog operationLog = new OperationLog();
        operationLog.setOperationObject(crmPrintTemplate.getTemplateName());
        operationLog.setOperationInfo("复制了审批模板:" + name);
        return operationLog;
    }

    @Override
    public Boolean updateCategoryId(Long newCategoryId, Long oldCategoryId) {
        if (ObjectUtil.isNotNull(newCategoryId) && ObjectUtil.isNotNull(oldCategoryId)) {
            boolean exists = this.lambdaQuery().eq(OaExaminePrintTemplate::getConfigId, oldCategoryId).exists();
            if (exists) {
                return this.lambdaUpdate()
                        .set(OaExaminePrintTemplate::getConfigId, newCategoryId)
                        .in(OaExaminePrintTemplate::getConfigId, oldCategoryId)
                        .update();
            }
        }
        return false;
    }


}
